import json
import sys
import os
import time
import traceback
from collections import defaultdict
from datetime import datetime
import random

import openpyxl
import pandas as pd
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QSizePolicy, QMessageBox

from UI.MaxxerWindow import Ui_MainWindow
from calculator.process_src_data import process_data, PCB_DICT, OPO_DICT
from UI.UI_AddUpside import Ui_Dialog as AddUpsideUIDialog
from UI.UI_UpsideOutput import Ui_Dialog as UpsideOutputUIDialog
from UI.UI_PRFOutput import Ui_PRFDialog
from UI.UI_DataChange import Ui_Dialog as DataChangeUIDialog

if hasattr(sys, '_MEIPASS'):
    data_path = os.path.join(sys._MEIPASS, "data")
else:
    data_path = "data"
json_file_path = os.path.join(data_path, "file_path.json")


class AddUpsideDialog(QtWidgets.QDialog, AddUpsideUIDialog):
    def __init__(self, parent=None):
        super(AddUpsideDialog, self).__init__(parent)
        self.setupUi(self)

        # 设置验证器，只允许大写字母和数字
        regex = QtCore.QRegularExpression("[A-Z0-9]+")
        validator = QtGui.QRegularExpressionValidator(regex)
        self.lineEdit.setValidator(validator)

    def get_data(self):
        s_code = self.lineEdit.text()
        qty = self.spinBox.value()
        year = self.dateEdit.date().year()
        week = self.spinBox_2.value()
        return s_code, qty, year, week


class UpsideOutputDialog(QtWidgets.QDialog, UpsideOutputUIDialog):
    def __init__(self, file_path, parent=None):
        super(UpsideOutputDialog, self).__init__(parent)
        self.setupUi(self)
        self.result_file = file_path
        self.load_excel_data(file_path)
        self.add_tips()
        self.pushButton.clicked.connect(self.load_result_file)

    def load_result_file(self):
        """加载Excel数据到表格"""
        try:
            # 显示frame控件
            self.tableWidget_2.show()
            self.set_window_size_percentage()
            # 读取Excel文件
            df = pd.read_excel(self.result_file)

            # 设置表格的行数和列数
            self.tableWidget_2.setRowCount(df.shape[0])
            self.tableWidget_2.setColumnCount(df.shape[1])

            # 设置表头
            self.tableWidget_2.setHorizontalHeaderLabels(df.columns)

            # 填充数据并居中显示
            for row in range(df.shape[0]):
                for col in range(df.shape[1]):
                    # 获取单元格值
                    cell_value = df.iloc[row, col]

                    # 处理NaN值
                    if pd.isna(cell_value):
                        item_value = ""
                    else:
                        # 检查是否是浮点数且没有小数部分
                        if isinstance(cell_value, float) and cell_value.is_integer():
                            item_value = str(int(cell_value))
                        else:
                            item_value = str(cell_value)

                    # 创建表格项并设置值
                    item = QtWidgets.QTableWidgetItem(item_value)
                    item.setForeground(QtGui.QBrush(QtGui.QColor(255, 255, 255)))
                    # 设置文本居中
                    item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)

                    # 添加到表格
                    self.tableWidget_2.setItem(row, col, item)

            # 调整列宽自适应
            self.tableWidget_2.horizontalHeader().setSectionResizeMode(
                QtWidgets.QHeaderView.ResizeMode.Stretch
            )

            # 调整行高自适应
            self.tableWidget_2.verticalHeader().setSectionResizeMode(
                QtWidgets.QHeaderView.ResizeMode.ResizeToContents
            )

            # 启用排序功能
            self.tableWidget_2.setSortingEnabled(True)

            # 设置表头居中
            for i in range(self.tableWidget_2.columnCount()):
                header_item = self.tableWidget_2.horizontalHeaderItem(i)
                if header_item:
                    header_item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)

        except Exception as e:
            QtWidgets.QMessageBox.critical(
                self,
                "错误",
                f"加载Excel文件失败: {str(e)}"
            )

    def load_excel_data(self, file_path):
        try:
            # 读取Excel前5列
            df = pd.read_excel(file_path, usecols=range(5))
            # 添加去重逻辑 - 基于所有列删除重复行
            df = df.drop_duplicates().reset_index(drop=True)

            # 设置表格维度
            self.tableWidget.setRowCount(len(df))
            self.tableWidget.setColumnCount(5)  # 确保有5列

            # 设置表头
            headers = ["Week", "S-Code", "PRF", "Full kit Num", "Full kit Rate"]
            self.tableWidget.setHorizontalHeaderLabels(headers)

            # 填充数据
            for row_idx, row in df.iterrows():
                for col_idx in range(5):
                    value = row[col_idx]

                    # 处理不同列的数据格式
                    if col_idx in [2, 3]:  # PRF和Full kit Num列
                        cell_value = str(int(value)) if pd.notna(value) else ""
                    elif col_idx == 4:  # Full kit Rate列
                        if pd.isna(value):
                            cell_value = ""
                        else:
                            # 将小数转换为百分比字符串
                            cell_value = f"{value * 100:.2f}%" if isinstance(value, float) else str(value)
                    else:  # 其他文本列
                        cell_value = str(value) if pd.notna(value) else ""

                    item = QtWidgets.QTableWidgetItem(cell_value)
                    item.setForeground(QtGui.QBrush(QtGui.QColor(255, 255, 255)))
                    # 设置文本居中
                    item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter |
                                          QtCore.Qt.AlignmentFlag.AlignVCenter)

                    self.tableWidget.setItem(row_idx, col_idx, item)

            # 设置列宽自适应填充
            self.tableWidget.horizontalHeader().setSectionResizeMode(
                QtWidgets.QHeaderView.ResizeMode.Stretch
            )

            # 设置行高自适应
            self.tableWidget.verticalHeader().setSectionResizeMode(
                QtWidgets.QHeaderView.ResizeMode.ResizeToContents
            )

            # 设置表头居中
            for i in range(self.tableWidget.columnCount()):
                header_item = self.tableWidget.horizontalHeaderItem(i)
                if header_item:
                    header_item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)

            # 启用排序功能
            self.tableWidget.setSortingEnabled(True)
            self.tableWidget_2.setHidden(True)

        except Exception as e:
            QtWidgets.QMessageBox.critical(
                self,
                "错误",
                f"读取Excel文件失败:\n{str(e)}",
                QtWidgets.QMessageBox.StandardButton.Ok
            )

    def add_tips(self):
        self.textBrowser.setStyleSheet(
            "QTextBrowser { "
            "background-color: #C0C4C8; "
            "color: black; "
            "font-size: 14px; "
            "font-family: Arial, sans-serif; "
            "}"
        )
        tip1_list = [f"{k}({float(v)})" for k, v in OPO_DICT.items()]
        tip1_slist = ",".join(tip1_list)
        tip1 = f"Pls confirm with ODM current inventory &OPO ETA [{tip1_slist}]"
        tip2_slist = ",".join(PCB_DICT.keys())
        tip2 = f"({tip2_slist}) are PCB/LTIM Parts, Pls confirm whether can be used for current Scode build."
        context = "Warning:\n\n"
        if tip1_slist:
            context += f"{tip1}.\n"
        if tip2_slist:
            context += f"{tip2}.\n"
        self.textBrowser.setText(
            context
        )

    def set_window_size_percentage(self, width_percent=70, height_percent=80):
        """
        设置窗口大小为屏幕尺寸的百分比

        参数:
            window: QtWidgets.QWidget 实例
            width_percent: 宽度百分比 (0-100)
            height_percent: 高度百分比 (0-100)
        """
        # 获取主屏幕的可用几何区域（不包括任务栏）
        screen = QtWidgets.QApplication.primaryScreen()
        screen_geometry = screen.availableGeometry()

        # 计算窗口大小
        width = int(screen_geometry.width() * width_percent / 100)
        height = int(screen_geometry.height() * height_percent / 100)

        # 设置窗口大小
        self.resize(width, height)

        # 计算并设置窗口位置（居中显示）
        x = (screen_geometry.width() - width) // 2
        y = (screen_geometry.height() - height) // 2
        self.move(x, y)


class DataChangeDialog(QtWidgets.QDialog, DataChangeUIDialog):
    def __init__(self, parent=None):
        super(DataChangeDialog, self).__init__(parent)
        self.setupUi(self)
        correction_data_path = './data/DataChange.xlsx'
        self.correction_data_path = os.path.realpath(os.path.join(os.path.dirname(__file__), correction_data_path))
        # 预定义的sheet名称映射
        self.sheet_names = ["PEGA_BOM", "PEGA_SSDD", "PTI_BOM", "PTI_SSDD"]
        # 初始化表格样式
        self.init_table_style()
        self.setup_table_actions()

    def setup_table_actions(self):
        """为所有表格添加操作支持"""
        for sheet_name in self.sheet_names:
            if not hasattr(self, f"tableWidget_{sheet_name}"):
                continue

            table = getattr(self, f"tableWidget_{sheet_name}")

            # 启用右键菜单
            table.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
            table.customContextMenuRequested.connect(self.show_context_menu)

    def show_context_menu(self, pos):
        """显示右键菜单"""
        table = self.sender()
        menu = QtWidgets.QMenu()

        add_action = menu.addAction("Append Row")
        add_action.triggered.connect(lambda: self.add_new_row(table))

        menu.exec(table.viewport().mapToGlobal(pos))

    def add_new_row(self, table=None):
        """添加新行到指定表格"""
        if table is None:
            table = self.sender()

        # 在最后插入空行
        row_position = table.rowCount()
        table.insertRow(row_position)

        # 初始化单元格
        for col in range(table.columnCount()):
            item = QtWidgets.QTableWidgetItem("")
            item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignVCenter | QtCore.Qt.AlignmentFlag.AlignLeft)
            table.setItem(row_position, col, item)

        # 自动滚动到新行
        table.scrollToItem(table.item(row_position, 0))

    def init_table_style(self):
        """完整的PyQt6样式配置"""
        for sheet_name in self.sheet_names:
            if not hasattr(self, f"tableWidget_{sheet_name}"):
                continue

            table = getattr(self, f"tableWidget_{sheet_name}")

            # 列配置
            header = table.horizontalHeader()
            header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Stretch)
            header.setDefaultAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

            # 行配置
            table.verticalHeader().setDefaultSectionSize(30)

            # 启用交替行颜色
            table.setAlternatingRowColors(True)

    def load_data(self):
        """加载数据（在主窗口的槽函数中调用示例）"""
        try:
            wb = openpyxl.load_workbook(self.correction_data_path)
            for sheet_name in self.sheet_names[:4]:
                table = getattr(self, f"tableWidget_{sheet_name}", None)
                if not table:
                    continue

                table.setRowCount(0)
                ws = wb[sheet_name]

                for row_idx, row in enumerate(ws.iter_rows(min_row=2, max_col=2, values_only=True), 1):
                    if not any(cell is not None for cell in row):
                        continue

                    table.insertRow(row_idx - 1)
                    for col, value in enumerate(row):
                        item = QtWidgets.QTableWidgetItem(str(value) if value else "")
                        item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignVCenter | QtCore.Qt.AlignmentFlag.AlignLeft)
                        table.setItem(row_idx - 1, col, item)
            wb.close()
        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "错误", f"加载失败: {str(e)}")

        self.resize(711, 428)

    def save_data(self):
        """保存数据（在主窗口的槽函数中调用示例）"""
        try:
            wb = openpyxl.load_workbook(self.correction_data_path)
            for sheet_name in self.sheet_names[:4]:
                table = getattr(self, f"tableWidget_{sheet_name}", None)
                if not table:
                    continue

                ws = wb[sheet_name]
                if ws.max_row > 1:
                    ws.delete_rows(2, ws.max_row)

                for row in range(table.rowCount()):
                    current = table.item(row, 0).text() if table.item(row, 0) else ""
                    change_to = table.item(row, 1).text() if table.item(row, 1) else ""
                    ws.append([current, change_to])

            wb.save(self.correction_data_path)
            wb.close()
            QtWidgets.QMessageBox.information(self, "成功", "保存成功")
        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "错误", f"保存失败: {str(e)}")


class PRFOutputDialog(QtWidgets.QDialog, Ui_PRFDialog):
    def __init__(self, data_type, data_paths, parent=None):
        super(PRFOutputDialog, self).__init__(parent)
        self.setupUi(self)
        self.data_type = data_type
        self.data_paths = data_paths
        self.tk_file_path = ""  # 保存文件路径
        self.cs_file_path = ""  # 保存文件路径
        self.merge_file_path = ""  # 保存文件路径
        self.init_settings()
        self.calculate_summary()

        # 连接按钮点击信号
        self.pushButton.clicked.connect(self.load_excel_data)
        self.pushButton_2.clicked.connect(self.load_excel_data)
        self.pushButton_3.clicked.connect(self.load_excel_data)

    def init_settings(self):
        output_path = self.data_paths.get('output', os.getcwd())
        tk_ssdd = self.data_paths.get('ssdd')
        cs_ssdd = self.data_paths.get('consigned_ssdd')
        self.pushButton.setHidden(True)
        self.pushButton_2.setHidden(True)
        if tk_ssdd:
            self.pushButton.setHidden(False)
            self.tk_file_path = output_path + f'/{self.data_type}-products_summary_output.xlsx'
        if cs_ssdd:
            self.pushButton_2.setHidden(False)
            self.cs_file_path = output_path + f'/{self.data_type}-cs-products_summary_output.xlsx'
        self.merge_file_path = output_path + f'/{self.data_type}-merged-products_summary_output.xlsx'
        # 初始隐藏frame及其内部控件
        self.frame.hide()

        # 设置tableWidget的列宽自适应
        header = self.tableWidget.horizontalHeader()
        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Stretch)

        # 设置表格大小策略为扩展
        self.tableWidget.setSizePolicy(
            QtWidgets.QSizePolicy.Policy.Expanding,
            QtWidgets.QSizePolicy.Policy.Expanding
        )

        # 连接窗口大小变化信号到调整列宽的函数
        self.resizeEvent = self.on_resize
        self.resize(1080, 100)

    def on_resize(self, event):
        """窗口大小变化时重新调整列宽"""
        super(PRFOutputDialog, self).resizeEvent(event)
        self.tableWidget.horizontalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeMode.Stretch
        )

    def calculate_summary(self):
        """计算Excel文件中的统计数据并显示在表格中（第一行为 TK，第二行为 CS）"""
        try:
            # 初始化默认值
            tk_vals = (0, 0, 0, 0)
            cs_vals = (0, 0, 0, 0)

            # 只有当文件路径存在且非空时才读取和计算TK数据
            if self.tk_file_path and os.path.exists(self.tk_file_path):
                tk_df = pd.read_excel(self.tk_file_path)

                def summarize(df):
                    if df.shape[1] < 8:  # 确保至少有8列
                        return (0, 0, 0, 0)
                    # 根据实际列名调整索引
                    # Baseline Demand (索引2) 非空计数
                    baseline_demand_not_empty = df.iloc[:, 2].notna().sum()
                    # Full kit Rate (索引4) 为 "100.0%" 的计数
                    full_kit_rate_100_percent = (df.iloc[:, 4] == "100.0%").sum()
                    # Baseline Demand非空计数减去Full kit Rate为100%的计数
                    baseline_minus_full_kit = baseline_demand_not_empty - full_kit_rate_100_percent
                    # Recovery Week (索引7) 非空且 Adjusted Full kit Rate (索引6) 为 "100.0%" 的计数
                    recovery_not_empty_adjusted_100 = ((df.iloc[:, 7].notna()) & (df.iloc[:, 6] == "100.0%")).sum()
                    return (baseline_demand_not_empty, full_kit_rate_100_percent, baseline_minus_full_kit,
                            recovery_not_empty_adjusted_100)

                tk_vals = summarize(tk_df)

            # 只有当文件路径存在且非空时才读取和计算CS数据
            if self.cs_file_path and os.path.exists(self.cs_file_path):
                cs_df = pd.read_excel(self.cs_file_path)

                def summarize(df):
                    if df.shape[1] < 8:  # 确保至少有8列
                        return (0, 0, 0, 0)
                    # 根据实际列名调整索引
                    baseline_demand_not_empty = df.iloc[:, 2].notna().sum()
                    full_kit_rate_100_percent = (df.iloc[:, 4] == "100.0%").sum()
                    baseline_minus_full_kit = baseline_demand_not_empty - full_kit_rate_100_percent
                    recovery_not_empty_adjusted_100 = ((df.iloc[:, 7].notna()) & (df.iloc[:, 6] == "100.0%")).sum()
                    return (baseline_demand_not_empty, full_kit_rate_100_percent, baseline_minus_full_kit,
                            recovery_not_empty_adjusted_100)

                cs_vals = summarize(cs_df)

            # 合并两个表格（如果两个文件都存在）
            if (self.tk_file_path and os.path.exists(self.tk_file_path) and
                    self.cs_file_path and os.path.exists(self.cs_file_path)):

                # 重新读取数据
                tk_df = pd.read_excel(self.tk_file_path)
                cs_df = pd.read_excel(self.cs_file_path)

                def merge_dataframes(tk_df, cs_df):
                    """合并两个DataFrame，按照Week和S-Code匹配"""
                    # 确保列数足够
                    if tk_df.shape[1] < 8 or cs_df.shape[1] < 8:
                        return pd.DataFrame()

                    # 添加来源标识
                    tk_df['_source'] = 'TK'
                    cs_df['_source'] = 'CS'

                    # 合并两个DataFrame
                    merged_df = pd.concat([tk_df, cs_df], ignore_index=True)

                    # 定义合并函数
                    def merge_group(group):
                        if len(group) == 1:
                            # 只有一行，直接返回
                            return group.iloc[0]
                        else:
                            # 有多行，需要合并
                            def parse_percentage(value):
                                """将百分比字符串转换为浮点数"""
                                if isinstance(value, str) and '%' in value:
                                    return float(value.replace('%', ''))
                                return float(value) if pd.notna(value) else float('inf')

                            # 找到Adjusted Full kit Rate最小的行（索引6）
                            min_idx = group.iloc[:, 6].apply(parse_percentage).idxmin()
                            min_row = group.loc[min_idx].copy()

                            # 合并Shortage Materials列（索引5）
                            shortage_materials = []
                            for idx, row in group.iterrows():
                                if pd.notna(row.iloc[5]):
                                    materials = str(row.iloc[5]).split(',')
                                    shortage_materials.extend([mat.strip() for mat in materials if mat.strip()])

                            # 去重并拼接
                            if shortage_materials:
                                unique_materials = list(dict.fromkeys(shortage_materials))  # 保持顺序的去重
                                min_row.iloc[5] = ', '.join(unique_materials)
                            else:
                                min_row.iloc[5] = ''

                            return min_row

                    # 按Week（索引0）和S-Code（索引1）分组合并
                    if merged_df.shape[1] >= 2:  # 确保有足够的列
                        # 分组处理
                        grouped = merged_df.groupby([merged_df.iloc[:, 0], merged_df.iloc[:, 1]])
                        result_rows = []

                        for (week, s_code), group in grouped:
                            merged_row = merge_group(group)
                            result_rows.append(merged_row)

                        if result_rows:
                            merged_result = pd.DataFrame(result_rows)
                            # 删除临时来源列
                            if '_source' in merged_result.columns:
                                merged_result = merged_result.drop('_source', axis=1)
                            return merged_result

                    return pd.DataFrame()

                # 执行合并
                merged_df = merge_dataframes(tk_df, cs_df)

                # 保存合并结果
                if not merged_df.empty and hasattr(self, 'merge_file_path') and self.merge_file_path:
                    # 确保目录存在
                    os.makedirs(os.path.dirname(self.merge_file_path), exist_ok=True)
                    merged_df.to_excel(self.merge_file_path, index=False)
                    print(f"合并文件已保存到: {self.merge_file_path}")

            # 设置两行数据
            self.tableWidget.setRowCount(2)
            white_brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))

            # TK 行（第0行）
            for col, val in enumerate(tk_vals):
                item = QtWidgets.QTableWidgetItem(str(val))
                item.setForeground(white_brush)
                item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
                self.tableWidget.setItem(0, col, item)

            # CS 行（第1行）
            for col, val in enumerate(cs_vals):
                item = QtWidgets.QTableWidgetItem(str(val))
                item.setForeground(white_brush)
                item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
                self.tableWidget.setItem(1, col, item)

            # 设置垂直表头标签区分 TK/CS
            self.tableWidget.setVerticalHeaderLabels(["TK Summary", "CS Summary"])

        except Exception as e:
            QtWidgets.QMessageBox.critical(
                self,
                "统计错误",
                f"计算统计数据时出错: {str(e)}"
            )

    def load_excel_data(self):
        """加载Excel数据到表格"""
        result_file_map = {
            "pushButton": self.tk_file_path,
            "pushButton_2": self.cs_file_path,
            "pushButton_3": self.merge_file_path
        }
        try:
            # 显示frame控件
            self.frame.show()
            self.set_window_size_percentage()

            file_path = result_file_map[self.sender().objectName()]
            df = pd.read_excel(file_path)

            # 设置表格的行数和列数
            self.tableWidget_2.setRowCount(df.shape[0])
            self.tableWidget_2.setColumnCount(df.shape[1])
            self.tableWidget_2.setHorizontalHeaderLabels(df.columns)

            # 🚩 关键优化：关闭更新和排序
            self.tableWidget_2.setUpdatesEnabled(False)
            self.tableWidget_2.setSortingEnabled(False)

            # 填充数据
            for row in range(df.shape[0]):
                for col in range(df.shape[1]):
                    cell_value = df.iloc[row, col]
                    if pd.isna(cell_value):
                        item_value = ""
                    elif isinstance(cell_value, float) and cell_value.is_integer():
                        item_value = str(int(cell_value))
                    else:
                        item_value = str(cell_value)

                    item = QtWidgets.QTableWidgetItem(item_value)
                    item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
                    self.tableWidget_2.setItem(row, col, item)

            # 🚩 填充完成后再开启排序和更新
            self.tableWidget_2.setSortingEnabled(True)
            self.tableWidget_2.setUpdatesEnabled(True)

            # 调整列宽（保留 Stretch）
            self.tableWidget_2.horizontalHeader().setSectionResizeMode(
                QtWidgets.QHeaderView.ResizeMode.Stretch
            )

            # 🚩 去掉逐行计算行高，改为固定或手动调整一次
            # self.tableWidget_2.verticalHeader().setSectionResizeMode(
            #     QtWidgets.QHeaderView.ResizeMode.ResizeToContents
            # )
            self.tableWidget_2.resizeRowsToContents()

            # 设置表头居中
            for i in range(self.tableWidget_2.columnCount()):
                header_item = self.tableWidget_2.horizontalHeaderItem(i)
                if header_item:
                    header_item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)

        except Exception as e:
            QtWidgets.QMessageBox.critical(
                self,
                "错误",
                f"加载Excel文件失败: {str(e)}"
            )

    def set_window_size_percentage(self, width_percent=60, height_percent=80):
        """
        设置窗口大小为屏幕尺寸的百分比

        参数:
            window: QtWidgets.QWidget 实例
            width_percent: 宽度百分比 (0-100)
            height_percent: 高度百分比 (0-100)
        """
        # 获取主屏幕的可用几何区域（不包括任务栏）
        screen = QtWidgets.QApplication.primaryScreen()
        screen_geometry = screen.availableGeometry()

        # 计算窗口大小
        width = int(screen_geometry.width() * width_percent / 100)
        height = int(screen_geometry.height() * height_percent / 100)

        # 设置窗口大小
        self.resize(width, height)

        # 计算并设置窗口位置（居中显示）
        x = (screen_geometry.width() - width) // 2
        y = (screen_geometry.height() - height) // 2
        self.move(x, y)


class ProcessThread(QtCore.QThread):
    finished = QtCore.pyqtSignal(int, str)

    def __init__(self, data_type, data_paths, end_week,
                 upside_data=None):
        super().__init__()
        self.data_type = data_type
        self.data_paths = data_paths
        self.end_week = end_week
        self.upside_data = upside_data

    def run(self):
        try:
            return_code, return_msg = process_data(self.data_type,
                                                   self.data_paths,
                                                   self.end_week,
                                                   self.upside_data
                                                   )
            self.finished.emit(return_code, return_msg)
        except Exception as e:
            print(e)
            print(traceback.format_exc())
            self.finished.emit(1, f"Failed: {e}")


class Particle(QtWidgets.QLabel):
    """粒子小点"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFixedSize(4, 4)
        self.setStyleSheet("background-color: rgba(255,255,255,180); border-radius: 2px;")
        self.anim = QtCore.QPropertyAnimation(self, b"pos", self)
        self.anim.setDuration(random.randint(4000, 8000))
        self.anim.setLoopCount(-1)

    def start(self, start_pos, end_pos):
        self.move(start_pos)
        self.anim.setStartValue(start_pos)
        self.anim.setEndValue(end_pos)
        self.anim.start()


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.progress_bar = QtWidgets.QProgressBar(self)
        self.file_name = ""
        self.result_dir = ""
        self.data_type = ""
        self.ssdd_data_path = ""
        self.consigned_ssdd_data_path = ''
        self.prf_data_path = ""
        self.bom_data_path = ""
        self.dcr_data_path = ""
        self.inventory_data_path = ""
        self.data_paths = {}
        self.dialog = None
        self.data_valid_flag = True  # 合法性标记
        self.setupUi(self)
        self.process_thread = None
        # 启用右键菜单策略
        self.tableWidget.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
        self.tableWidget.customContextMenuRequested.connect(self.show_context_menu)

        # 创建右键菜单
        self.context_menu = QtWidgets.QMenu(self)
        self.clear_all_action = self.context_menu.addAction("Clear All")
        self.clear_row_action = self.context_menu.addAction("Delete Current Row")
        self.clear_all_action.triggered.connect(self.clear_all_contents)
        self.clear_row_action.triggered.connect(self.clear_current_row)
        # 加载JSON数据
        self.json_data = self.load_json_data()

        self.init_signals()
        self.init_settings()

        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.update_status_bar)
        self.elapsed_time = 0

    # def resizeEvent(self, event):
    #     super().resizeEvent(event)
    #     current_width = self.width()
    #     max_progress_bar_width = current_width - 256
    #     percent_progress_bar_width = int(current_width * 0.7)
    #     final_pro_bar_width = min(max_progress_bar_width, percent_progress_bar_width)
    #     self.progress_bar.setFixedWidth(final_pro_bar_width)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.drag_position = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
            event.accept()
        else:
            super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.MouseButton.LeftButton and self.drag_position is not None:
            self.move(event.globalPosition().toPoint() - self.drag_position)
            event.accept()
        else:
            super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.drag_position = None
            event.accept()
        else:
            super().mouseReleaseEvent(event)

    def init_signals(self):
        # 连接TreeWidget的点击信号
        self.treeWidget.itemClicked.connect(self.on_tree_item_clicked)

        self.pushButton.clicked.connect(self.choose_attachment_bom)
        self.pushButton_2.clicked.connect(self.choose_attachment_ssdd)
        self.pushButton_12.clicked.connect(self.choose_consigned_ssdd)
        self.pushButton_3.clicked.connect(self.choose_attachment_prf)
        self.pushButton_11.clicked.connect(self.choose_attachment_dcr)
        self.pushButton_10.clicked.connect(self.choose_attachment_inventory)
        self.pushButton_4.clicked.connect(self.start_process)

        self.pushButton_5.clicked.connect(self.choose_result_file_path)
        # 连接 dateChanged 信号到槽函数
        self.dateEdit_2.dateChanged.connect(self.update_line_edit)
        self.comboBox.currentTextChanged.connect(self.update_paths)

        # 连接 MPM 开始按钮 到弹窗槽函数
        self.pushButton_6.clicked.connect(self.show_add_upside_dialog)
        # 连接 Stakeholder 开始按钮 到弹窗槽函数
        self.pushButton_7.clicked.connect(self.start_stakeholder)

        self.tableWidget.cellChanged.connect(self.highlight_cells)
        self.comboBox_2.currentTextChanged.connect(self.update_table_column)
        self.pushButton_8.clicked.connect(self.show_change_data_dialog)
        self.pushButton_9.clicked.connect(self.start_stakeholder)

    def init_settings(self):
        self.setWindowTitle("Out Maxxxer Tool")
        self.setWindowIcon(QtGui.QIcon('UI/icon002.ico'))

        self.resize_table_columns()
        self.dateEdit_2.setMinimumSize(QtCore.QSize(90, 20))
        end_date = self.dateEdit_2.date()
        week_number = end_date.weekNumber()

        end_week = f"{week_number[1]}{week_number[0]:02d}"
        self.lineEdit_5.setText(end_week)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        self.treeWidget.headerItem().setTextAlignment(0, Qt.AlignmentFlag.AlignCenter)  # 第一列居中
        self.treeWidget.expandAll()
        self.init_home_page_effects()
        self.resize_to_screen_percent(0.45)  # 设置为屏幕的45%
        # self.center_on_screen()
        # 设置整个表格不可编辑
        self.tableWidget.setEditTriggers(
            self.tableWidget.EditTrigger.NoEditTriggers  # PyQt6 枚举写法
        )
        self.dateEdit_2.setDate(QtCore.QDate.currentDate().addDays(30))
        self.add_progress_bar()
        self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
        self.update_paths()
        default_data_path = "./data"
        if not os.path.exists(default_data_path):
            os.makedirs(default_data_path)

    def add_progress_bar(self):
        self.progress_bar.setMaximum(100)

        self.progress_bar.setVisible(False)
        self.statusbar.addPermanentWidget(self.progress_bar)
        self.progress_bar.setValue(10)
        # self.progress_bar.setVisible(True)

    def show_context_menu(self, pos: QtCore.QPoint):
        # 转换坐标到全局坐标系
        global_pos = self.tableWidget.viewport().mapToGlobal(pos)
        self.context_menu.exec(global_pos)

    # 在您的窗口类中添加这个方法
    def on_tree_item_clicked(self, item, column):
        """处理TreeWidget项点击事件，切换StackedWidget页面"""
        # 获取点击项的文本
        item_text = item.text(0)

        # 根据文本切换到对应页面
        if item_text == "Out Maxxer":
            self.stackedWidget.setCurrentWidget(self.page_home)
        elif item_text == "PRF Projection":
            self.stackedWidget.setCurrentWidget(self.page_prf)
        elif item_text == "Up-side Projection":
            self.stackedWidget.setCurrentWidget(self.page_upside)

    def init_home_page_effects(self):
        """美化 home_page 页面，加动画和粒子效果"""

        # 背景（用深紫色图片填充整个 page_home）
        bg = QtWidgets.QLabel(self.page_home)
        bg.setPixmap(QtGui.QPixmap("UI/bg_purple.png").scaled(
            self.page_home.size(),
            QtCore.Qt.AspectRatioMode.IgnoreAspectRatio,
            QtCore.Qt.TransformationMode.SmoothTransformation
        ))
        bg.setGeometry(0, 0, self.page_home.width(), self.page_home.height())
        bg.lower()  # 放到最底层

        # 半透明遮罩
        overlay = QtWidgets.QLabel(self.page_home)
        overlay.setStyleSheet("background-color: rgba(0,0,0,0);")
        overlay.setGeometry(0, 0, self.page_home.width(), self.page_home.height())
        overlay.lower()


        # 动态粒子
        for _ in range(25):
            p = Particle(self.page_home)
            start = QtCore.QPoint(random.randint(0, self.page_home.width()),
                                  random.randint(0, self.page_home.height()))
            end = QtCore.QPoint(start.x(), random.randint(0, self.page_home.height()))
            p.start(start, end)

    def clear_all_contents(self):
        """清空整个表格内容（保留表头）"""
        reply = QtWidgets.QMessageBox.question(
            self, 'Clear All Confirmation',
            'Are you sure you want to clear all data?',
            QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No
        )
        if reply == QtWidgets.QMessageBox.StandardButton.Yes:
            self.tableWidget.clearContents()
            self.tableWidget.setRowCount(0)  # 可选：同时重置行数
            self.highlight_cells()

    def clear_current_row(self):
        current_row = self.tableWidget.currentRow()
        if current_row >= 0:
            reply = QtWidgets.QMessageBox.question(
                self, 'Confirm Deletion',
                'Are you sure you want to delete the current row?',
                QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No
            )
            if reply == QtWidgets.QMessageBox.StandardButton.Yes:
                self.tableWidget.removeRow(current_row)
                self.highlight_cells()

    def resize_table_columns(self):
        # 设置所有列的宽度自适应
        self.tableWidget.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Stretch)

    def update_line_edit(self, date):
        week_number = date.weekNumber()

        end_week = f"{week_number[1]}{week_number[0]:02d}"
        self.lineEdit_5.setText(end_week)

    def start_process(self):
        data_type = self.comboBox.currentText()
        ssdd_data_path = self.lineEdit_2.text()
        consigned_ssdd_data_path = self.lineEdit_8.text()
        prf_data_path = self.lineEdit_3.text()
        bom_data_path = self.lineEdit.text()
        self.data_type = data_type
        self.ssdd_data_path = ssdd_data_path
        self.consigned_ssdd_data_path = consigned_ssdd_data_path
        self.prf_data_path = prf_data_path
        self.bom_data_path = bom_data_path
        self.dcr_data_path = self.lineEdit_7.text()
        self.inventory_data_path = self.lineEdit_6.text()
        self.result_dir = self.lineEdit_4.text()
        data_paths = {"bom": bom_data_path, "ssdd": ssdd_data_path, "prf": prf_data_path,
                      "dcr": self.dcr_data_path, "consigned_ssdd": self.consigned_ssdd_data_path,
                      "inventory": self.inventory_data_path, 'output': self.result_dir}
        self.data_paths = data_paths
        if not self.check_path(data_type):
            return
        end_date = self.dateEdit_2.date()
        week_number = end_date.weekNumber()

        end_week = f"{week_number[1]}{week_number[0]:02d}"
        print(end_week)
        print("开始处理")
        self.elapsed_time = 0
        self.timer.start(1000)  # 每隔1秒触发一次
        try:
            self.process_thread = ProcessThread(data_type, data_paths,
                                                end_week)
            self.process_thread.finished.connect(self.on_finished)
            self.process_thread.start()
        except Exception as e:
            print(e)
            print(traceback.format_exc())

    def update_status_bar(self):
        try:
            self.elapsed_time += 1
            message = f"Processing, elapsed time {self.elapsed_time} seconds"
            print(message)  # 添加调试信息
            self.statusbar.showMessage(message)
        except Exception as e:
            print(e)
            print(traceback.format_exc())

    def check_path(self, data_type):
        widget_list = [(self.label, self.lineEdit),
                       (self.label_2, self.lineEdit_2),
                       (self.label_12, self.lineEdit_8),
                       (self.label_3, self.lineEdit_3),
                       ]
        if not os.path.exists(self.result_dir):
            QtWidgets.QMessageBox.warning(self, "提示", f"Please select the correct Output path, "
                                                        f"the current selected path does not exist.")
            return False
        for widget in widget_list:
            label, line_edit = widget
            if line_edit == self.lineEdit_8 and not line_edit.text():
                continue
            if data_type not in line_edit.text() and label is not self.label and line_edit.text().strip() != "":
                QtWidgets.QMessageBox.warning(self, "提示", f"请选择正确的{data_type} {label.text()} 文件!")
                return False
            if line_edit.text().strip() != "" and not os.path.exists(line_edit.text()):
                QtWidgets.QMessageBox.warning(self, "提示", f"{label.text()} 文件不存在!")
                return False
        return True

    def on_finished(self, return_code, return_msg):
        self.timer.stop()  # 停止计时器
        self.statusbar.clearMessage()  # 清空状态栏
        if return_code == 0:
            self.save_src_file_path()

        self.show_prf_output_dialog(self.data_type, self.data_paths)
        # QtWidgets.QMessageBox.information(self, "提示", f"{return_msg}! Time elapsed {self.elapsed_time} seconds.")

    def choose_attachment_bom(self):
        file_name_bom = QtWidgets.QFileDialog.getOpenFileName(self, "选择附件BOM表", ".", "Excel文件(*.xlsx)")
        self.lineEdit.setText(file_name_bom[0])

    def choose_attachment_ssdd(self):
        file_name_ssdd = QtWidgets.QFileDialog.getOpenFileName(self, "选择附件SSDD表", ".", "Excel文件(*.xlsx)")
        self.lineEdit_2.setText(file_name_ssdd[0])

    def choose_consigned_ssdd(self):
        file_name_ssdd = QtWidgets.QFileDialog.getOpenFileName(self, "选择附件SSDD表", ".", "Excel文件(*.xlsx)")
        self.lineEdit_8.setText(file_name_ssdd[0])

    def choose_attachment_prf(self):
        file_name_prf = QtWidgets.QFileDialog.getOpenFileName(self, "选择附件PRF表", ".", "Excel文件(*.xlsx)")
        self.lineEdit_3.setText(file_name_prf[0])

    def choose_attachment_dcr(self):
        file_name_dcr = QtWidgets.QFileDialog.getOpenFileName(self, "选择附件DCR表", ".", "Excel文件(*.xlsx)")
        self.lineEdit_7.setText(file_name_dcr[0])

    def choose_attachment_inventory(self):
        file_name_inventory = QtWidgets.QFileDialog.getOpenFileName(self, "选择附件inventory表", ".",
                                                                    "Excel文件(*.xlsx *.xlsb)")
        self.lineEdit_6.setText(file_name_inventory[0])

    def choose_result_file_path(self):
        try:
            self.result_dir = QtWidgets.QFileDialog.getExistingDirectory(self, "选择结果文件所在目录", ".",
                                                                         QtWidgets.QFileDialog.Option.ShowDirsOnly)

            current_date_time_str = datetime.now().strftime("%Y%m%d%H%M%S")
            current_year = datetime.now().year
            self.file_name = os.path.join(self.result_dir,
                                          current_date_time_str + ".xlsx")
            self.lineEdit_4.setText(self.result_dir)
        except Exception as e:
            print(e)
            print(traceback.format_exc())

    def show_add_upside_dialog(self):
        row_count = self.tableWidget.rowCount()
        print(f"row_count: {row_count}")
        if row_count >= 6:
            QtWidgets.QMessageBox.warning(self, "Warning", "The number of rows exceeds 6, please delete some rows.")
            return
        self.dialog = AddUpsideDialog(self)
        if self.dialog.exec() == QtWidgets.QDialog.DialogCode.Accepted:
            # 获取弹窗中的数据
            s_code, qty, year, week = self.dialog.get_data()
            week_name = f"{year}{week:02d}"
            print(f"S-code: {s_code}, Qty: {qty}, Year: {year}, Week: {week}")
            nmanufacturer = self.comboBox_2.currentText()
            if not s_code:
                QtWidgets.QMessageBox.warning(self, "Warning", "Please enter S-code")
                return

            self.tableWidget.insertRow(row_count)
            self.tableWidget.setItem(row_count, 0, QtWidgets.QTableWidgetItem(s_code))
            self.tableWidget.setItem(row_count, 1, QtWidgets.QTableWidgetItem(str(qty)))
            self.tableWidget.setItem(row_count, 2, QtWidgets.QTableWidgetItem(str(week_name)))
            self.tableWidget.setItem(row_count, 3, QtWidgets.QTableWidgetItem(str(nmanufacturer)))
        else:
            print("弹窗被取消")

    def save_src_file_path(self, is_save=True):
        # json_file_path = "data/file_path.json"
        if not os.path.exists(json_file_path):
            print("文件不存在")
            return
        try:
            with open(json_file_path, "r") as f:
                file_path_dict = json.load(f)

        except Exception as e:
            print(e)
            print(traceback.format_exc())
            return

        if self.data_type and is_save:
            file_path_dict[self.data_type]["BOM"] = self.bom_data_path
            file_path_dict[self.data_type]["PRF"] = self.prf_data_path
            file_path_dict[self.data_type]["SSDD"] = self.ssdd_data_path
            file_path_dict[self.data_type]["CONSIGNED_SSDD"] = self.consigned_ssdd_data_path
            file_path_dict[self.data_type]["DCR"] = self.dcr_data_path
            file_path_dict[self.data_type]["Inventory"] = self.inventory_data_path
            # 把数据保存到json文件中
            with open(json_file_path, "w") as f:
                json.dump(file_path_dict, f, indent=4)
        return file_path_dict

    def get_table_widget_data(self):
        """
        获取QTableWidget中的所有数据并返回为字典格式
        :return: 字典，键为列名，值为对应列的数据列表
        """
        rows = self.tableWidget.rowCount()
        columns = self.tableWidget.columnCount()
        data = {}

        # 获取列名
        header = self.tableWidget.horizontalHeader()
        for column in range(columns):
            column_name = header.model().headerData(column, QtCore.Qt.Orientation.Horizontal)
            if column_name is None:
                column_name = f"Column_{column}"
            data[column_name] = []

        # 获取每一行的数据
        for row in range(rows):
            for column in range(columns):
                column_name = header.model().headerData(column, QtCore.Qt.Orientation.Horizontal)
                if column_name is None:
                    column_name = f"Column_{column}"
                item = self.tableWidget.item(row, column)
                if item is not None:
                    value = item.text()
                    if column_name == "Qty":
                        value = int(item.text())
                    data[column_name].append(value)
                else:
                    data[column_name].append(None)  # 如果单元格为空，添加None

        return data

    def start_stakeholder(self):

        sender = self.sender()

        if sender is None:
            return
        # is_lead_time = True if sender.objectName() == "pushButton_9" else False

        row_count = self.tableWidget.rowCount()
        print(f"row_count: {row_count}")
        if not self.is_data_valid():
            QtWidgets.QMessageBox.warning(self, "Error", "Error: Duplicate data or missing values.")
            return
        if row_count == 0:
            QtWidgets.QMessageBox.warning(self, "Error", "No data!")
            return

        data_type = self.comboBox_2.currentText()
        real_data_type = data_type.split("(")[0]
        self.data_type = real_data_type
        file_path_dict = self.save_src_file_path(is_save=False)
        bom_data_path = file_path_dict[real_data_type]["BOM"]
        ssdd_data_path = file_path_dict[real_data_type]["SSDD"]
        cs_ssdd_data_path = file_path_dict[real_data_type]["CONSIGNED_SSDD"]
        prf_data_path = file_path_dict[real_data_type]["PRF"]
        inventory_data_path = file_path_dict[real_data_type]["Inventory"]
        dcr_data_path = file_path_dict[real_data_type]["DCR"]
        if not self.check_files_path([bom_data_path, ssdd_data_path, prf_data_path]):
            return
        self.result_dir = 'data/'
        data_paths = {
            "bom": bom_data_path,
            "ssdd": ssdd_data_path,
            "consigned_ssdd": cs_ssdd_data_path,
            "prf": prf_data_path,
            "inventory": inventory_data_path,
            "dcr": dcr_data_path,
            "output": self.result_dir
        }
        upside_data = self.get_table_widget_data()
        week_list = [int(week) for week in upside_data.get("Week", [])]
        upside_data["Scode"] = upside_data.pop("S-code")
        upside_data["周名称"] = upside_data.pop("Week")
        upside_data["数量"] = upside_data.pop("Qty")

        print(upside_data)

        end_week = str(max(week_list))
        print(end_week)
        print("开始处理")
        # return
        self.elapsed_time = 0
        self.timer.start(1000)  # 每隔1秒触发一次
        try:
            self.process_thread = ProcessThread(data_type, data_paths,
                                                end_week, upside_data)
            self.process_thread.finished.connect(self.on_stakeholder_finished)
            self.process_thread.start()
        except Exception as e:
            print(e)
            print(traceback.format_exc())

    def check_files_path(self, path_list):
        for path in path_list:
            if not os.path.exists(path):
                QtWidgets.QMessageBox.warning(self, "Warning", f"MPM files does not exist")
                return False
        return True

    def show_data_dialog(self, upside_res_file_path):
        dialog = UpsideOutputDialog(upside_res_file_path, self, )
        dialog.exec()

    def on_stakeholder_finished(self, return_code, return_msg):
        self.timer.stop()  # 停止计时器
        self.statusbar.clearMessage()  # 清空状态栏
        if return_code != 0:
            QtWidgets.QMessageBox.information(self, "提示", f"{return_msg}! Time elapsed {self.elapsed_time} seconds.")
            return
        upside_res_file_path = os.path.join(self.result_dir, f"{self.data_type}-upside_output.xlsx")
        print(f"PCB:{PCB_DICT}, OPO:{OPO_DICT}")
        self.show_data_dialog(upside_res_file_path)

    def highlight_cells(self):
        """更新数据合法性标记的入口函数"""
        # 阻断信号防止递归
        self.tableWidget.blockSignals(True)

        # 核心处理逻辑
        has_invalid = False
        combo_dict = defaultdict(int)

        # 第一次遍历：统计重复组合
        for row in range(self.tableWidget.rowCount()):
            col0 = self._get_cell_text(row, 0).lower()
            col2 = self._get_cell_text(row, 2).lower()
            combo_dict[(col0, col2)] += 1

        # 第二次遍历：设置颜色并检测非法
        for row in range(self.tableWidget.rowCount()):
            col0 = self._get_cell_text(row, 0).lower()
            col2 = self._get_cell_text(row, 2).lower()
            current_combo = (col0, col2)

            for col in range(self.tableWidget.columnCount()):
                item = self.tableWidget.item(row, col)
                if not item:
                    continue

                # 标黄空单元格
                if not item.text().strip():
                    item.setBackground(QtGui.QColor(255, 255, 0))  # 黄色
                    has_invalid = True
                # else:
                #     item.setBackground(QtGui.QColor(255, 255, 255))  # 重置背景

                # 标红重复组合（仅处理0、2列）
                if col in [0, 2] and combo_dict[current_combo] >= 2:
                    item.setBackground(QtGui.QColor(255, 0, 0))  # 红色
                    has_invalid = True

        # 更新标记变量
        self.data_valid_flag = not has_invalid

        # 恢复信号连接
        self.tableWidget.blockSignals(False)

    def _get_normalized_text(self, row, col):
        """获取标准化文本（空值返回空字符串）"""
        item = self.tableWidget.item(row, col)
        return item.text().lower().strip() if item else ""

    def _get_cell_text(self, row, col):
        """安全获取单元格文本"""
        item = self.tableWidget.item(row, col)
        return item.text().strip() if item else ""

    # 主程序使用的接口方法
    def is_data_valid(self):
        """获取当前数据合法性状态"""
        return self.data_valid_flag

    def update_validity_flag(self):
        """更新数据合法性标记的入口函数"""
        # 阻断信号防止递归
        self.tableWidget.blockSignals(True)

        # 核心处理逻辑
        has_invalid = False
        combo_dict = defaultdict(int)

        # 第一次遍历：统计重复组合
        for row in range(self.tableWidget.rowCount()):
            col0 = self._get_cell_text(row, 0).lower()
            col2 = self._get_cell_text(row, 2).lower()
            combo_dict[(col0, col2)] += 1

    def update_table_column(self, text):
        """将第四列所有单元格设置为combobox的值"""
        target_col = 3  # 第四列（索引从0开始）

        # 确保表格至少有1列（如果列数不足则自动扩展）
        if self.tableWidget.columnCount() <= target_col:
            self.tableWidget.setColumnCount(target_col + 1)

        # 遍历所有行设置值
        for row in range(self.tableWidget.rowCount()):
            # 获取或创建单元格
            if not self.tableWidget.item(row, target_col):
                self.tableWidget.setItem(row, target_col, QtWidgets.QTableWidgetItem())

            # 设置单元格文本
            self.tableWidget.item(row, target_col).setText(text)

    def resize_to_screen_percent(self, ratio: float):
        """
        按屏幕可用区域比例调整窗口尺寸
        :param ratio: 0~1之间的比例值
        """
        # 获取主屏幕的可用区域（排除任务栏等）
        screen_geo = QtWidgets.QApplication.primaryScreen().availableGeometry()

        # 计算目标尺寸
        # target_width = int(screen_geo.width() * ratio)
        # target_height = int(screen_geo.height() * ratio)

        # 设置窗口尺寸
        self.setFixedSize(1080, 720)

    def center_on_screen(self):
        """将窗口居中显示"""
        # 获取屏幕几何信息
        screen_geo = QtWidgets.QApplication.primaryScreen().availableGeometry()

        # 计算居中坐标
        window_geo: QtCore.QRect = self.frameGeometry()
        x = (screen_geo.width() - window_geo.width()) // 2
        y = (screen_geo.height() - window_geo.height()) // 2

        # 移动窗口到计算位置
        self.move(x, y)

    def show_change_data_dialog(self, ):
        try:
            dialog = DataChangeDialog(self)
            dialog.load_data()  # 打开时自动加载
            if dialog.exec() == QtWidgets.QDialog.DialogCode.Accepted:
                dialog.save_data()  # 点击OK时保存
        except Exception as e:
            print(e)
            print(traceback.format_exc())

    def show_prf_output_dialog(self,data_type, data_paths):
        # file_path = r"test/0908/PTI-products_summary_output3.xlsx"
        try:
            dialog = PRFOutputDialog(data_type, data_paths, self, )
            dialog.exec()
        except Exception as e:
            print(e)
            print(traceback.format_exc())

    def load_json_data(self):
        """加载JSON文件数据"""
        try:
            with open(json_file_path, 'r') as f:
                return json.load(f)
        except Exception as e:
            print(f"Error loading JSON file: {e}")
            return {
                "PTI": {"PRF": "", "SSDD": "", "BOM": "", "DCR": "", "Inventory": ""},
                "PEGA": {"PRF": "", "SSDD": "", "BOM": "", "DCR": "", "Inventory": ""}
            }

    def update_paths(self):
        """根据comboBox选项更新路径显示和类属性"""
        current_type = self.comboBox.currentText()
        data = self.json_data.get(current_type, {})

        # 更新lineEdit显示和类属性
        self.bom_data_path = data.get("BOM", "")
        self.lineEdit.setText(self.bom_data_path)

        self.ssdd_data_path = data.get("SSDD", "")
        self.lineEdit_2.setText(self.ssdd_data_path)

        self.consigned_ssdd_data_path = data.get("CONSIGNED_SSDD", "")
        self.lineEdit_8.setText(self.consigned_ssdd_data_path)

        self.prf_data_path = data.get("PRF", "")
        self.lineEdit_3.setText(self.prf_data_path)

        self.dcr_data_path = data.get("DCR", "")
        self.lineEdit_7.setText(self.dcr_data_path)

        self.inventory_data_path = data.get("Inventory", "")
        self.lineEdit_6.setText(self.inventory_data_path)


if __name__ == '__main__':

    def excepthook(exc_type, exc_value, exc_tb):
        """全局异常处理钩子"""
        # 打印到控制台
        print(f"未捕获的异常: {exc_type.__name__}: {exc_value}")
        traceback.print_tb(exc_tb)

        # 如果有logger，记录到日志
        try:
            if 'window' in globals() and hasattr(window, 'logger'):
                window.logger.error(f"未捕获的异常: {exc_type.__name__}: {exc_value}")
                window.logger.error("".join(traceback.format_tb(exc_tb)))
        except:
            pass

        # 显示错误对话框（可选）
        try:
            error_msg = f"{exc_type.__name__}: {exc_value}"
            QMessageBox.critical(None, "未处理的错误",
                                 f"程序发生未处理的错误:\n{error_msg}\n")
        except:
            pass
    # 设置全局异常处理钩子
    sys.excepthook = excepthook

    try:
        app = QtWidgets.QApplication(sys.argv)
        # 切换到浅色主题
        window = MainWindow()
        window.show()
        sys.exit(app.exec())
    except Exception as e:
        print(f"程序启动失败: {e}")
        traceback.print_exc()
        QMessageBox.critical(None, "启动错误", f"程序启动失败:\n{e}")
        sys.exit(1)

